package cn.com.anysdk.map.service;

// 引入相关配置、异常、模型类和依赖库
import cn.com.anysdk.map.config.BaiduMapConfig;
import cn.com.anysdk.map.exception.MapApiException;
import cn.com.anysdk.map.exception.MapCoordinateException;
import cn.com.anysdk.map.model.*;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.core5.http.ParseException;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.net.URIBuilder;

import java.io.IOException;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;

/**
 * 百度地图服务实现
 */
@Slf4j
public class BaiduMapService implements MapService {

    // 百度地图API基础URL
    private static final String BASE_URL = "https://api.map.baidu.com";
    // 地理编码API
    private static final String GEOCODING_URL = BASE_URL + "/geocoding/v3/";
    // 逆地理编码API
    private static final String REVERSE_GEOCODING_URL = BASE_URL + "/reverse_geocoding/v3/";
    // 坐标转换API
    private static final String COORDINATE_CONVERT_URL = BASE_URL + "/geoconv/v2/";
    // 静态地图API
    private static final String STATIC_MAP_URL = BASE_URL + "/staticimage/v2";

    private final BaiduMapConfig config; // 百度地图配置
    private final CloseableHttpClient httpClient; // HTTP客户端
    private final ObjectMapper objectMapper; // JSON解析器

    // 构造方法，注入配置和HTTP客户端
    public BaiduMapService(BaiduMapConfig config, CloseableHttpClient httpClient) {
        this.config = config;
        this.httpClient = httpClient;
        this.objectMapper = new ObjectMapper();
    }


    /**
     * 获取静态地图URL
     * @param address 地址
     * @param zoom 缩放级别
     * @return 静态地图URL
     */
    @Override
    public String getStaticMapUrl(String address, int zoom) {
        try {
            // 先通过地理编码获取坐标
            LocationResult locationResult = geocode(address);
            if (!locationResult.isSuccess()) {
                log.error("Failed to get location for address: {}", address);
                return null;
            }

            Location location = locationResult.getLocation();
            
            // 构建静态地图URL
            Map<String, String> params = new HashMap<>();
            params.put("ak", config.getAk());
            params.put("center", location.getLongitude() + "," + location.getLatitude());
            params.put("zoom", String.valueOf(zoom));
            params.put("width", "1024");
            params.put("height", "1024");
            params.put("markers", location.getLongitude() + "," + location.getLatitude());

            URIBuilder builder = new URIBuilder(STATIC_MAP_URL);
            params.forEach(builder::addParameter);

            return builder.build().toString();
        } catch (Exception e) {
            log.error("Failed to generate static map URL", e);
            return null;
        }
    }

    /**
     * 地址转坐标（地理编码）
     * @param address 地址
     * @return LocationResult
     */
    @Override
    public LocationResult geocode(String address) {
        try {
            Map<String, String> params = new HashMap<>();
            params.put("address", address);
            params.put("output", "json");
            params.put("ak", config.getAk());

            // 调用百度地理编码API
            JsonNode response = doGet(GEOCODING_URL, params);
            JsonNode result = response.get("result");
            
            if (result == null) {
                return LocationResult.error("Geocoding failed: " + response.get("message").asText());
            }

            Location location = new Location();
            location.setLatitude(result.get("location").get("lat").asDouble());
            location.setLongitude(result.get("location").get("lng").asDouble());
            location.setCoordinateType(CoordinateType.BD09); // 百度坐标系

            return LocationResult.success(location, address);
        } catch (Exception e) {
            log.error("Geocoding failed", e);
            return LocationResult.error("Geocoding failed: " + e.getMessage());
        }
    }

    /**
     * 坐标转地址（逆地理编码）
     * @param location 坐标
     * @return AddressResult
     */
    @Override
    public AddressResult reverseGeocode(Location location) {
        try {
            Map<String, String> params = new HashMap<>();
            params.put("location", location.getLatitude() + "," + location.getLongitude());
            params.put("output", "json");
            params.put("ak", config.getAk());

            // 调用百度逆地理编码API
            JsonNode response = doGet(REVERSE_GEOCODING_URL, params);
            JsonNode result = response.get("result");
            
            if (result == null) {
                return AddressResult.error("Reverse geocoding failed: " + response.get("message").asText());
            }

            return AddressResult.success(
                result.get("formatted_address").asText(),
                location
            );
        } catch (Exception e) {
            log.error("Reverse geocoding failed", e);
            return AddressResult.error("Reverse geocoding failed: " + e.getMessage());
        }
    }

    /**
     * 坐标系转换
     * @param location 原始坐标
     * @param fromType 原始坐标系
     * @param toType 目标坐标系
     * @return LocationResult
     */
    @Override
    public LocationResult convertCoordinate(Location location, CoordinateType fromType, CoordinateType toType) {
        try {
            // 百度地图只支持 WGS84、GCJ02 到 BD09 的转换
            if (toType != CoordinateType.BD09) {
                throw new MapCoordinateException("Baidu Map only supports conversion to BD09 coordinate system");
            }

            Map<String, String> params = new HashMap<>();
            params.put("coords", location.getLongitude() + "," + location.getLatitude());
            params.put("from", fromType == CoordinateType.WGS84 ? "1" : "3");
            params.put("to", "5"); // BD09
            params.put("output", "json");
            params.put("ak", config.getAk());

            // 调用百度坐标转换API
            JsonNode response = doGet(COORDINATE_CONVERT_URL, params);
            JsonNode result = response.get("result");
            
            if (result == null || !result.isArray() || result.isEmpty()) {
                return LocationResult.error("Coordinate conversion failed: " + response.get("message").asText());
            }

            JsonNode converted = result.get(0);
            Location convertedLocation = new Location();
            convertedLocation.setLatitude(converted.get("y").asDouble());
            convertedLocation.setLongitude(converted.get("x").asDouble());
            convertedLocation.setCoordinateType(CoordinateType.BD09);

            return LocationResult.success(convertedLocation, null);
        } catch (Exception e) {
            log.error("Coordinate conversion failed", e);
            return LocationResult.error("Coordinate conversion failed: " + e.getMessage());
        }
    }

    /**
     * 获取地图服务商类型
     * @return MapProviderType
     */
    @Override
    public MapProviderType getProviderType() {
        return MapProviderType.BAIDU;
    }

    /**
     * 通用GET请求方法，处理百度地图API调用
     * @param url API地址
     * @param params 参数
     * @return JsonNode
     * @throws IOException
     * @throws URISyntaxException
     * @throws ParseException
     */
    private JsonNode doGet(String url, Map<String, String> params) throws IOException, URISyntaxException, ParseException {
        URIBuilder builder = new URIBuilder(url);
        params.forEach(builder::addParameter);

        HttpGet request = new HttpGet(builder.build());
        request.setHeader("Accept", "application/json");

        try (var response = httpClient.execute(request)) {
            String responseBody = EntityUtils.toString(response.getEntity());
            JsonNode jsonResponse = objectMapper.readTree(responseBody);

            // 检查API返回状态码
            if (jsonResponse.get("status").asInt() != 0) {
                throw new MapApiException(
                    "Baidu Map API error: " + jsonResponse.get("message").asText(),
                    response.getCode()
                );
            }

            return jsonResponse;
        }
    }
}

